{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def main():\n",
    "    \"\"\" 完成一系列操作的主函数 \"\"\"\n",
    "    load() # 调用load函数\n",
    "\n",
    "    while True: \n",
    "        print(\"我的日记系统v1.0\".center(20, '-') +\n",
    "            \"\\n1. 添加日记     \\\n",
    "            \\n2. 删除日记      \\\n",
    "            \\n3. 修改日记信息   \\\n",
    "            \\n4. 查询日记信息   \\\n",
    "            \\n5. 显示所有日记信息\\\n",
    "            \\n6. 退出\")\n",
    "        try:\n",
    "            if (menu_option := int(input(\"请输入您需要的功能选项:\"))-1) == 5: # ValueError\n",
    "                save()\n",
    "                print(\"退出\")\n",
    "                break\n",
    "        except ValueError:\n",
    "            print(\"请你从新输入一个数字: \")\n",
    "            continue\n",
    "        #? func + () -> func()\n",
    "        func_ls = [add, remove, modify, search, show_all]\n",
    "        func_ls[menu_option]()\n",
    "        #? 为什么不把 () 放到 func_ls 里面?\n",
    "        #? func() -> 执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "def a(): return print(1+1)\n",
    "def b(): return print(1+2)\n",
    "def c(): return print(1+3)\n",
    "ls = [a, b, c]\n",
    "for i in range(3):\n",
    "    ls[i]()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sub_add():\n",
    "    \"\"\" 让用户输入日记内容 \"\"\"\n",
    "    middle=[] #? 中间变量\n",
    "    cnt=0 #? 行号\n",
    "    while diary:=input(':'):  \n",
    "        #? ':'提示用户输入, 不输入任何字符按回车结束\n",
    "        middle.append(str(cnt)+' '+diary)\n",
    "        cnt+=1\n",
    "    middle.append('end_of_diary') \n",
    "    # ? 添加一篇日记结束的标志\n",
    "    return middle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def add():\n",
    "    \"\"\" 让用户输入整篇日记 \"\"\"\n",
    "    global diary_ls\n",
    "    #! 不声明下一句不能添加  why?  如果局部要对全局变量*修改*，则在局部声明该全局变量\n",
    "    diary_ls.extend(['date:'+input(' ˙ω˙ 今天是几月几号?(eg.6/8): '),\n",
    "                    'wheather:'+input('今天天气怎么样: '),\n",
    "                    'mood:'+input('今天心情怎么样: '),\n",
    "                    'theme:'+input('日记的主题是: ')]) #? 字符串list\n",
    "    print('好,现在开始愉快地写日记吧!')\n",
    "    diary_ls.extend(sub_add())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# global\n",
    "def a():\n",
    "    # global关键字用来在函数或其他局部作用域中使用全局变量。\n",
    "    # 但是如果不修改全局变量也可以不使用global关键字\n",
    "    print(global_)\n",
    "\n",
    "def b():\n",
    "    global global_\n",
    "    print(global_)\n",
    "    print(global_:=1)\n",
    "global_=0\n",
    "# a()\n",
    "# b()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def save():\n",
    "    \"\"\" 保存 diary_ls 中的数据到 txt \"\"\"\n",
    "    #? 以\"写\"的方式,打开当前目录下的'diary.txt'\n",
    "    with open('./diary.txt', 'w', encoding='utf-8') as d:\n",
    "        d.write('\\n'.join(diary_ls))\n",
    "        #? 把 dairy_ls 转换成字符串,中间用换行回车隔开,再写入文件\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def search():\n",
    "    \"\"\" 根据索引查找date所在行 \"\"\"\n",
    "    while True:\n",
    "        try:\n",
    "            (choice := int(input('请输入索引:\\n1. 日期\\n2. 主题'))) in [1, 2]\n",
    "            break\n",
    "        except ValueError: # 确保用户输入的是1或2\n",
    "            print(\"请你输入一个数字: 1 或 2\")\n",
    "            continue\n",
    "    if not choice-1:\n",
    "        s = input('请输入日期: ') \n",
    "        # 以日期为搜索标志\n",
    "        # global diary_ls # 非必要\n",
    "        index = 0 # 找到了就要返回的下标值\n",
    "        for i in range(len(diary_ls)): # 下标从0 -> len(diary_ls)-1 遍历 diary_ls\n",
    "            if (aim1 := diary_ls[i]) == (aim2 := 'date:'+s): \n",
    "                # eg. 输入 6/9 -> date:6/9 再去和diary_ls里面的比较\n",
    "                index = i # 相等的时候得到下标值\n",
    "                break\n",
    "        else: # 如果循环了一圈都没找到\n",
    "            print('没有找到!')\n",
    "            return -1 # 返回 -1 表示没找的, 这里就退出函数了\n",
    "        i = index # 下标付给 i 作返回值, index 拿去作为循环变量\n",
    "        while diary_ls[index].strip() != 'end_of_diary': \n",
    "            # # IndexError: 'end_of_diary\\n'\n",
    "            print(diary_ls[index]) # 打印找到的结果\n",
    "            index += 1\n",
    "        return i # 返回找到的下标值\n",
    "    else:\n",
    "        s = input('请输入主题: ') # ***\n",
    "        index = 0\n",
    "        for i in range(len(diary_ls)):\n",
    "            if diary_ls[i] == 'theme:'+s: # ***\n",
    "                index = i\n",
    "                break\n",
    "        else:\n",
    "            print('没有找到!')\n",
    "            return -1\n",
    "        i = (index :=index-3) # ***\n",
    "        while diary_ls[index].strip() != 'end_of_diary':\n",
    "            print(diary_ls[index])\n",
    "            index += 1\n",
    "        return i #? 返回的还是date"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def remove(index=-2):\n",
    "    \"\"\" 删除 \"\"\"\n",
    "    #? 如果remove有参数传入 index=传入的参数 , 否则调用searc去找\n",
    "    if index != -2 or (index := search()) != -1: #* 没找的,或没传入下标就不执行\n",
    "        while diary_ls[index] != 'end_of_diary': # 从date删到end_of_diary\n",
    "            del diary_ls[index]\n",
    "        del diary_ls[index] # 删除 end_of_diary\n",
    "            #! index += 1 after del ls shrink or list index out of range\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def modify():\n",
    "    \"\"\" 修改日记内容 \"\"\"\n",
    "    # 调用search去找要修改的,没找到就show_menu\n",
    "    #? 为什么不直接remove\n",
    "    if (index := search()) != -1: \n",
    "        remove(index+4) # 找到了就删掉其类容\n",
    "        global diary_ls\n",
    "        diary_ls=diary_ls[:index+4]+sub_add()+diary_ls[index+4:]\n",
    "        # 然添加新的\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def load():\n",
    "    \"\"\" 加载 txt 中数据到 dairy_ls \"\"\"\n",
    "    #? 以\"添加\"的方式,打开当前目录下的'diary.txt'   创建新文件时，可用参数：w,w+,a,a+\n",
    "    #? 为什么不以\"读\"的方式打开文件 -> 不能新建文件\n",
    "    with open('./diary.txt', 'a+', encoding='utf-8') as d:\n",
    "        global diary_ls  # !必要\n",
    "        #? 读取文件全部类容->str, 去掉两边的空格和空行,按 \\n 分割->list\n",
    "        d.seek(0) #? 文件指针回到文件开头\n",
    "        diary_ls = d.read().strip().split('\\n')\n",
    "        print(diary_ls) # 输出以前的 diary_ls 里面的类容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def show_all():\n",
    "    \"\"\" 打印所有日记信息 \"\"\"\n",
    "#     global diary_ls\n",
    "    for i in diary_ls:\n",
    "        print([i,''][i=='end_of_diary'])\n",
    "    # i== 'end_of_diary' -> print('')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "diary_ls = []\n",
    "if __name__ == '__main__': #? 程序主入口\n",
    "    try:\n",
    "        main()\n",
    "    except KeyboardInterrupt:\n",
    "        # Ctrl+C\n",
    "        ... # 不做任何事\n",
    "    finally:\n",
    "        print('谢谢使用 :)')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# 问题:  \n",
    "### * 全局变量  ->增加了耦合\n",
    "\n",
    "## 解决:  \n",
    "### * 创建一个只能生成一个对象的类  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "def singleton(cls):\n",
    "    _instance = {}\n",
    "\n",
    "    def inner():\n",
    "        if cls not in _instance:\n",
    "            _instance[cls] = cls()\n",
    "        return _instance[cls]\n",
    "    return inner\n",
    "\n",
    "\n",
    "@singleton\n",
    "class Diary(object):\n",
    "    __diary_ls = []\n",
    "\n",
    "    def __init__(self):\n",
    "        #* 主程序入口\n",
    "        try:\n",
    "            self.__main()\n",
    "        except KeyboardInterrupt:\n",
    "            ...\n",
    "        finally:\n",
    "            print('谢谢使用 :)')\n",
    "\n",
    "    def add(self):\n",
    "        Diary.__diary_ls.append('date:'+input(' ˙ω˙ 今天是几月几号?(eg.6/8): ')+'\\n'+\n",
    "                        'wheather:'+input('今天天气怎么样: ')+'\\n'+\n",
    "                        'mood:'+input('今天心情怎么样: ')+'\\n'+\n",
    "                        'name:'+input('日记的主题是: '))\n",
    "        print('好,现在开始愉快地写日记吧!')\n",
    "        cnt = 0\n",
    "        while diary := input('>'):\n",
    "            Diary.__diary_ls.append(str(cnt)+' '+diary)\n",
    "            cnt += 1\n",
    "        Diary.__diary_ls.append('end_of_diary\\n')\n",
    "\n",
    "    def save(self):\n",
    "        with open('./diary.txt', 'w', encoding='utf-8') as d:\n",
    "            d.write('\\n'.join(Diary.__diary_ls))\n",
    "\n",
    "    def search(self):\n",
    "        s = input('请输入日期: ')\n",
    "        index = 0\n",
    "        for i in range(len(Diary.__diary_ls)):\n",
    "            if (ls := Diary.__diary_ls[i]) == (aim := 'date:'+s):\n",
    "                index = i\n",
    "                break\n",
    "        else:\n",
    "            print('没有找到!')\n",
    "            return -1\n",
    "        i = index\n",
    "        while Diary.__diary_ls[index] != 'end_of_diary':\n",
    "            print(Diary.__diary_ls[index])\n",
    "            index += 1\n",
    "        return i\n",
    "\n",
    "    def remove(self, index=-2):\n",
    "        if index != -2 or (index := self.search()) != -1:\n",
    "            while Diary.__diary_ls[index] != 'end_of_diary':\n",
    "                del Diary.__diary_ls[index]\n",
    "            del Diary.__diary_ls[index]\n",
    "\n",
    "    def modify(self):\n",
    "        if (index := self.search()) != -1:\n",
    "            self.remove(index)\n",
    "            self.add()\n",
    "\n",
    "    def load(self):\n",
    "        with open('./diary.txt', 'r', encoding='utf-8') as d:\n",
    "            Diary.__diary_ls = d.read().strip().split('\\n')\n",
    "            print(Diary.__diary_ls)\n",
    "\n",
    "    def __main(self):\n",
    "        self.load()\n",
    "\n",
    "        while True:\n",
    "            print(\"我的日记系统v1.0\".center(20, '-') +\n",
    "                  \"\\n1. 添加日记     \\\n",
    "                \\n2. 删除日记      \\\n",
    "                \\n3. 修改日记信息   \\\n",
    "                \\n4. 查询日记信息   \\\n",
    "                \\n5. 显示所有日记信息\\\n",
    "                \\n6. 退出\")\n",
    "            try:\n",
    "                if (menu_option := int(input(\"请输入您需要的功能选项:\"))-1) == 5:\n",
    "                    self.save()\n",
    "                    print(\"退出\")\n",
    "                    break\n",
    "            except ValueError:\n",
    "                print(\"请你输入一个数字\")\n",
    "                continue\n",
    "\n",
    "            func_ls = [self.add, self.remove, self.modify,\n",
    "                       self.search, lambda:print(Diary.__diary_ls)]\n",
    "            func_ls[menu_option]()\n",
    "\n",
    "\n",
    "Diary1 = Diary()\n",
    "# print(__diary_ls)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "# 装饰器\n",
    "def singleton(cls): # 传入一个对象(obj)\n",
    "    _instance = {}\n",
    "\n",
    "    def inner():\n",
    "        if cls not in _instance: # 判断在 obj 在不在 _instance 里面\n",
    "            _instance[cls] = cls() # 不在的话就把临时对象加到 _instan 里面\n",
    "        return _instance[cls] # 如果已经有了就返回class\n",
    "    return inner"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "@singleton\n",
    "class Diary(object):\n",
    "    __diary_ls = [] # 类 私有的 成员变量\n",
    "\n",
    "    def __init__(self):\n",
    "        ...\n",
    "#         # * 主程序入口\n",
    "#         try:\n",
    "#             self.__main()\n",
    "#         except KeyboardInterrupt:\n",
    "#             ...\n",
    "#         finally:\n",
    "#             print('谢谢使用 :)')\n",
    "\n",
    "d1=Diary()\n",
    "d2=Diary()\n",
    "print(d1 is d2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 总结  \n",
    "### 1. global  \n",
    "### 如果局部要对全局变量*修改*，则在局部声明该全局变量  \n",
    "  \n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "幻灯片",
  "interpreter": {
   "hash": "ac59ebe37160ed0dfa835113d9b8498d9f09ceb179beaac4002f036b9467c963"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.4"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
